package usersimulation;

/* PSO - Particle Swarm Optimiser */
/* Part of the PSO project */
/* Dr Mark C. Sinclair, NPIC, Cambodia, v1.0, November 2006, mcs@ieee.org */
/* You may make use of this code for any purpose, but if you use it, */
/* you must cite the use: Sinclair, M.C., PSO Java Applet v1.0, */
/* http://uk.geocities.com/markcsinclair/pso.html, November, 2006 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class PSOApplet extends Applet {
	public void init() {
		// set defaults
		eval    = new Funct1();
		popSize = 50;
		maxV    = 2.0;
		c1      = 2.0;
		c2      = 2.0;
		dispP   = true;
		seed    = 1;

		// display parameter input interface
		inputInterface();
	}

	private void inputInterface() {
		setLayout(new BorderLayout());
		topPanel = new Panel();
		topPanel.setLayout(new BorderLayout());
		entryPanel = new Panel();
		entryPanel.setLayout(new GridLayout(0, 2));

		evalStrings = new String[] { "F1",         "F5" };
		evalEvals   = new Eval[]   { new Funct1(), new Funct5() };

		entryPanel.add(new Label("Eval:"));
		evalChoice = new Choice();
		for (int i=0; i<evalStrings.length; i++)
			evalChoice.add(evalStrings[i]);
		Class evalClass = eval.getClass();
		for (int i=0; i<evalEvals.length; i++)
			if (evalEvals[i].getClass().equals(evalClass)) {
				evalChoice.select(i);
				break;
			}
		entryPanel.add(evalChoice);

		entryPanel.add(new Label("Population size:"));
		popSizeTextField = new TextField(Integer.toString(popSize));
		entryPanel.add(popSizeTextField);

		entryPanel.add(new Label("Maximum velocity:"));
		maxVTextField = new TextField(Double.toString(maxV));
		entryPanel.add(maxVTextField);

		entryPanel.add(new Label("Constant c1:"));
		c1TextField = new TextField(Double.toString(c1));
		entryPanel.add(c1TextField);

		entryPanel.add(new Label("Constant c2:"));
		c2TextField = new TextField(Double.toString(c2));
		entryPanel.add(c2TextField);

		entryPanel.add(new Label("Display past best positions?"));
		dispPCheckbox = new Checkbox(null, dispP);
		entryPanel.add(dispPCheckbox);

		entryPanel.add(new Label("Random seed:"));
		seedTextField = new TextField(Long.toString(1));
		entryPanel.add(seedTextField);

		topPanel.add(entryPanel, BorderLayout.CENTER);

		enterButton = new Button("Entry Complete");
		enterButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				// Process PSO parameters
				eval = evalEvals[evalChoice.getSelectedIndex()];

				try {
					popSize = Integer.parseInt(popSizeTextField.getText());
					if (popSize <= 0) {
						showStatus("Enter population size > 0");
						return;
					}
				} catch(NumberFormatException nfe) {
					showStatus("Enter valid population size (an int)");
					return;
				}

				try {
					// maxV = Double.parseDouble(maxVTextField.getText());
					maxV = (new Double(maxVTextField.getText())).doubleValue();
					if (maxV <= 0.0) {
						showStatus("Enter maximum velocity > 0.0");
						return;
					}
				} catch(NumberFormatException nfe) {
					showStatus("Enter valid maximum velocity (a double)");
					return;
				}

				try {
					// c1 = Double.parseDouble(c1TextField.getText());
					c1 = (new Double(c1TextField.getText())).doubleValue();
					if ((c1 <= 0.0) || (c1 > 2.0)) {
						showStatus("Enter constant 0.0 > c1 <= 2.0");
						return;
					}
				} catch(NumberFormatException nfe) {
					showStatus("Enter valid constant c1 (a double)");
					return;
				}

				try {
					// c2 = Double.parseDouble(c2TextField.getText());
					c2 = (new Double(c2TextField.getText())).doubleValue();
					if ((c2 <= 0.0) || (c2 > 2.0)) {
						showStatus("Enter constant 0.0 > c2 <= 2.0");
						return;
					}
				} catch(NumberFormatException nfe) {
					showStatus("Enter valid constant c2 (a double)");
					return;
				}

				dispP = dispPCheckbox.getState();

				try {
					seed = Long.parseLong(seedTextField.getText());
				} catch(NumberFormatException nfe) {
					showStatus("Enter valid random seed (a long)");
					return;
				}

				// Remove input interface
				topPanel.setVisible(false);
				remove(topPanel);

				// Install PSO interface
				psoState = true;
				psoInterface();
				validate();
			}
		});
		topPanel.add(enterButton, BorderLayout.SOUTH);

		add(topPanel, BorderLayout.NORTH);

		showStatus("Enter PSO parameters, then press 'Entry Complete'");
	}

	private void psoInterface() {
		pSwarm     = new Swarm(eval, popSize, maxV, c1, c2, new Random(seed));
		swarmPanel = new SwarmPanel(pSwarm, eval, 0, 1, dispP);

		setLayout(new BorderLayout());
		add(swarmPanel, BorderLayout.CENTER);
		bottomPanel = new Panel();
		bottomPanel.setLayout(new GridLayout(0, 2));
		bottomPanel.add(new TrialLabel(pSwarm));
		bottomPanel.add(new FitLabel(pSwarm));

		startButton   = new Button("Start");
		startButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Start PSO or return to input interface (restart)
				if (startButton.getLabel().equals("Start")) {
					pSwarm.start();
					startButton.setLabel("Restart");
					showStatus("PSO running");
				} else {
					pSwarm.stop();

					// Remove PSO interface
					swarmPanel.setVisible(false);
					bottomPanel.setVisible(false);
					remove(swarmPanel);
					remove(bottomPanel);

					// Install input interface
					psoState = false;
					inputInterface();
					validate();
				}
			}
		});
		bottomPanel.add(startButton);

		pauseButton  = new Button("Pause");
		pauseButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Suspend or Resume
				if (pauseButton.getLabel().equals("Pause")) { 
					pSwarm.suspend();
					pauseButton.setLabel("Resume");
					showStatus("PSO paused");
				} else {
					pSwarm.resume();
					pauseButton.setLabel("Pause");
					showStatus("PSO running");
				}		    
			}
		});
		bottomPanel.add(pauseButton);

		add(bottomPanel, BorderLayout.SOUTH);

		showStatus("Press 'Start' to begin");
	}

	public void start() {
		if (psoState) {
			if (pauseButton.getLabel().equals("Pause")) {
				// Should be running
				pSwarm.resume();
				showStatus("PSO running");
			} else {
				// Should be paused
				showStatus("PSO paused");
			}
		}
	}

	public void stop() {
		if (psoState) {
			pSwarm.suspend();
		}
	}

	public void destroy() {
		if (psoState) {
			pSwarm.stop();
		}
	}

	static double trim(double d) {
		double f = 1000000.0;
		d       *= f;
		long dl  = Math.round(d);
		return((double) dl/f);
	}

	private boolean    psoState;         // in pso or input state?
	private Panel      topPanel;
	private Panel      entryPanel;
	private Choice     evalChoice;       // choose eval
	private String[]   evalStrings;
	private Eval[]     evalEvals;
	private TextField  seedTextField;    // enter seed
	private TextField  popSizeTextField; // enter popSize
	private TextField  maxVTextField;    // enter maxV
	private TextField  c1TextField;      // enter c1
	private TextField  c2TextField;      // enter c2
	private Checkbox   dispPCheckbox;    // set dispP
	private Button     enterButton;      // parameter entry complete
	private Eval       eval;
	private int        popSize;
	private double     maxV;
	private double     c1;
	private double     c2;
	private boolean    dispP;
	private long       seed;
	private Swarm      pSwarm;
	private SwarmPanel swarmPanel;
	private Panel      bottomPanel;
	private Button     startButton;
	private Button     pauseButton;

	private static final long serialVersionUID = 1L;
}
